/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = frct_expect.c                                      */
/*  NOTE      = 关联连接的管理                                     */
/*  DATE      = 2014/10/15 by yaohl                                */
/*******************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

#include "frcomm.h"
#include "frlog.h"
#include "frmem.h"
#include "frhash2.h"
#include "frtimer.h"
#include "conntrack.h"
#include "frame.h"
#include "frct_mng.h"

#include "frct_expect.h"

//删除时的timeout周期sec
#define APM_TIMEOUT_DEL_SEC     1

#define APM_CT_EXPECT_HASH_SIZE     1024
static Frhash_header        st_hash_expect_ct;
static pthread_spinlock_t   g_spin_exp_lock;

//relative timeout 时间sec
static unsigned long g_ul_relative_timeout=60L;

/*******************************************************************************
*  FUNC     :  节点内存释放
*  ARGS     :  p_st_node  (Frlist_node *)
*  RTN      :
*  NOTE     :
*******************************************************************************/
static void __list_node_free(Frlist_node *p_st_node)
{
    Frexp *p_st_exp = NULL;

    if(p_st_node == NULL || p_st_node->data == NULL)
        return;

    p_st_exp = (Frexp *)(p_st_node->data);
    if(p_st_exp->p_st_master )
    {
        __sync_fetch_and_sub(&p_st_exp->p_st_master->refcnt,1);
    }
    if(p_st_exp->p_st_timer_entry)
    {
        if(p_st_exp->p_st_timer_entry->p_st_event)
        {
            frtimer_del(p_st_exp->p_st_timer_entry->p_st_event);
            frtimer_free(p_st_exp->p_st_timer_entry->p_st_event);
        }
        frfree(p_st_exp->p_st_timer_entry);
    }
    frfree(p_st_exp);
    return;
}

/*******************************************************************************
*  FUNC     :  节点内容出力
*  ARGS     :  p_st_node       (Frlist_node *)
            :  p_outbuff       (出力内容指针)
            :  ui_buff_size    (出力内容空间)
*  RTN      :  比较一致返回0，其他返回1
*  NOTE     :
*******************************************************************************/
static void __list_node_show(Frlist_node *p_st_node, char *p_outbuff, unsigned int ui_buff_size)
{
    Frexp *p_st_exp_node = NULL;
    char arr_ip_src[56], arr_ip_dst[56];

    if(p_st_node == NULL || p_st_node->data == NULL || ui_buff_size <= 0 )
        return;

    p_st_exp_node = (Frexp *)(p_st_node->data);
    snprintf(arr_ip_src, sizeof(arr_ip_src), "%s", int_ntoa(p_st_exp_node->ui_src));
    snprintf(arr_ip_dst, sizeof(arr_ip_src), "%s", int_ntoa(p_st_exp_node->ui_dest));
    snprintf(p_outbuff, ui_buff_size, "%s->%s:%i master=%p"
           , arr_ip_src, arr_ip_dst, p_st_exp_node->us_dport
           , p_st_exp_node->p_st_master);
    return;
}

/*******************************************************************************
*  FUNC     :  组成哈希用key
*  ARGS     :  us_proto  (协议 tcp/udp)
*           :  ui_src    (原地址)
            :  ui_dest   (目的地址)
            :  us_dport  (目的端口)
            :  p_key     (保存地址)
            :  ui_key_size(保存地址长度)
*  RTN      :  key长度
*  NOTE     :
*******************************************************************************/
static int __frhash_expect_key(u_short us_proto, u_int ui_src, u_int ui_dest, u_short us_dport
                                , char *p_key, unsigned int ui_key_size)
{
    int i_idx=0, i_len;
    u_int *stupid_strict_aliasing_warnings=(u_int*)p_key;

    if(p_key == NULL || ui_key_size <= sizeof(u_int)*2+ sizeof(u_short)*2)
    {
        FRLOG_ERROR_DETAIL("expect hash key err");
        return 0;
    }

    i_len = sizeof(u_int);
    *stupid_strict_aliasing_warnings = ui_src;
    i_idx += i_len;

    *(u_int *) (p_key + i_idx) = ui_dest;
    i_idx += i_len;

    i_len = sizeof(u_short);
    *(u_short *) (p_key + i_idx) = us_dport;
    i_idx += i_len;

    *(u_short *) (p_key + i_idx) = us_proto;
    i_idx += i_len;

    return i_idx;
}

/*******************************************************************************
*  FUNC     :  关联连接初始化
*  ARGS     :
*  RTN      :  FR_SUCC :成功, FR_FAIL :失败
*  NOTE     :
*******************************************************************************/
int frct_expect_init(unsigned int ui_relative_timeout)
{
    int i_ret_fun=FR_FAIL;

    i_ret_fun = frhash2_create(&st_hash_expect_ct, APM_CT_EXPECT_HASH_SIZE);
    if(i_ret_fun != FR_SUCC)
        return FR_FAIL;
    g_ul_relative_timeout = ui_relative_timeout;
    pthread_spin_init(&g_spin_exp_lock, 0);
    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  时钟回调函数
*  ARGS     :  i_fd          (文件句柄)
            :  s_event       (事件号)
            :  p_arg         (入力参数数据)
*  RTN      :
*  NOTE     :
*******************************************************************************/
static void __frct_expect_timer_callback(int i_fd, short s_event, void *p_arg)
{
    Timer_entry *p_st_timer_entry=NULL;
    Frlist_node *p_st_node=(Frlist_node *)p_arg;
    Frexp *p_st_frexp = (Frexp *)FRLIST_GET_NODE_DATA(p_st_node);

    frdbg("__frct_expect_timer_callback run\n");
    p_st_timer_entry = p_st_frexp->p_st_timer_entry;

    //有引用
    if(p_st_frexp->ui_exp_refcnt > 0)
    {
        p_st_timer_entry->st_delay.tv_sec = g_ul_relative_timeout;
        p_st_timer_entry->st_delay.tv_usec = 0;
        frtimer_add(p_st_timer_entry->p_st_event, &p_st_timer_entry->st_delay);
        return;
    }

    //没有引用,删除该ct
    pthread_spin_lock(&g_spin_exp_lock);

    //防止其他地方再次加入timer
    frtimer_del(p_st_timer_entry->p_st_event);
    frhash2_del(&st_hash_expect_ct, p_st_node, __list_node_free);

    pthread_spin_unlock(&g_spin_exp_lock);
    frdbg("delete expct\n");

    return;
}

/*******************************************************************************
*  FUNC     :  增加关联连接
*  ARGS     :  us_proto  (协议 tcp/udp)
*           :  ui_src    (原地址)
*           :  ui_dest   (目的地址)
*           :  us_dport  (目的端口)
*           :  p_st_master  (Conntrack *)
*  RTN      :  FR_SUCC:成功, FR_FAIL:失败
*  NOTE     :
*******************************************************************************/
int frct_expect_add(u_short us_proto, u_int ui_src, u_int ui_dest, u_short us_dport, Conntrack *p_st_master)
{
    Frlist_node *p_st_node=NULL;
    char arr_hash_key[20];
    int i_hash_key_len=0;
    Frexp *p_st_exp=NULL;
    int i_ret = FR_FAIL, i_ret_fun;

    if(p_st_master==NULL)
        return i_ret;

    //确认关联连接是否存在
    i_hash_key_len = __frhash_expect_key(us_proto, ui_src, ui_dest, us_dport, arr_hash_key, sizeof(arr_hash_key));
    if(i_hash_key_len <=0)
        return i_ret;

    pthread_spin_lock(&g_spin_exp_lock);
    p_st_node = frhash2_getStr(&st_hash_expect_ct, arr_hash_key, i_hash_key_len);
    //p_st_exp = (Frexp *)FRLIST_GET_NODE_DATA(p_st_node);
    if(p_st_node)
    {
        //__sync_fetch_and_add(&p_st_exp->ui_exp_refcnt,1);
        pthread_spin_unlock(&g_spin_exp_lock);
        return FR_SUCC;
    }
    else
        pthread_spin_unlock(&g_spin_exp_lock);

    //增加管理连接
    p_st_exp = frmalloc(sizeof(Frexp));
    if(!p_st_exp)
        return i_ret;
    p_st_exp->ui_exp_refcnt =0;
    p_st_exp->us_proto = us_proto;
    p_st_exp->ui_src = ui_src;
    p_st_exp->ui_dest = ui_dest;
    p_st_exp->us_dport = us_dport;
    p_st_exp->p_st_master = p_st_master;

    p_st_node = frlist_node_create(p_st_exp);
    if(!p_st_node)
    {
        frfree(p_st_exp);
        return i_ret;
    }

    //激活时钟
    p_st_exp->p_st_timer_entry = frmalloc(sizeof(Timer_entry));
    if(p_st_exp->p_st_timer_entry == NULL)
    {
        FRLOG_ERROR_DETAIL("frmalloc err");
        frfree(p_st_exp);
        return i_ret;
    }
    memset(p_st_exp->p_st_timer_entry, '\0', sizeof(Timer_entry));
    p_st_exp->p_st_timer_entry->p_st_event = frtimer_new(g_p_st_ebase, __frct_expect_timer_callback, p_st_node);

    pthread_spin_lock(&g_spin_exp_lock);
    i_ret_fun = frhash2_addStr(&st_hash_expect_ct, arr_hash_key, i_hash_key_len, p_st_node);
    if(i_ret_fun == FR_FAIL)
    {
        goto ERROR;
    }

    frtimer_add(p_st_exp->p_st_timer_entry->p_st_event, &p_st_exp->p_st_timer_entry->st_delay);
    __sync_fetch_and_add(&p_st_master->refcnt,1);
    pthread_spin_unlock(&g_spin_exp_lock);

    return FR_SUCC;

ERROR:
    pthread_spin_unlock(&g_spin_exp_lock);
    if(p_st_node) frfree(p_st_node);
    if(p_st_exp) frfree(p_st_exp);

    return FR_FAIL;
}

/*******************************************************************************
*  FUNC     :  获取关联连接
*  ARGS     :  us_proto  (协议 tcp/udp)
*           :  ui_src    (原地址)
            :  ui_dest   (目的地址)
            :  us_dport  (目的端口)
*  RTN      :  ct:成功， NULL:失败
*  NOTE     :  增加关联连接的引用次数
*******************************************************************************/
Frexp *frct_expect_get(u_short us_proto, u_int ui_src, u_int ui_dest, u_short us_dport)
{
    Frlist_node *p_st_node=NULL;
    Frexp *p_st_exp=NULL;
    char arr_hash_key[20];
    int i_hash_key_len=0;

    i_hash_key_len = __frhash_expect_key(us_proto, ui_src, ui_dest, us_dport, arr_hash_key, sizeof(arr_hash_key));
    if(i_hash_key_len <=0)
        return NULL;

    pthread_spin_lock(&g_spin_exp_lock);
    p_st_node = frhash2_getStr(&st_hash_expect_ct, arr_hash_key, i_hash_key_len);
    p_st_exp = (Frexp *)FRLIST_GET_NODE_DATA(p_st_node);
    if(p_st_exp == NULL)
    {
        pthread_spin_unlock(&g_spin_exp_lock);
        return NULL;
    }

    //增加关联连接的引用次数
    __sync_fetch_and_add(&p_st_exp->ui_exp_refcnt,1);
    pthread_spin_unlock(&g_spin_exp_lock);

    return p_st_exp;
}

/*******************************************************************************
*  FUNC     :  减少关联连接的引用次数
*  ARGS     :  us_proto  (协议 tcp/udp)
*           :  ui_src    (原地址)
            :  ui_dest   (目的地址)
            :  us_dport  (目的端口)
*  RTN      :  FR_SUCC:成功, FR_FAIL:失败
*  NOTE     :
*******************************************************************************/
int frct_expect_put(u_short us_proto, u_int ui_src, u_int ui_dest, u_short us_dport)
{
    Frlist_node *p_st_node=NULL;
    Frexp *p_st_exp;
    char arr_hash_key[20];
    int i_hash_key_len=0;

    i_hash_key_len = __frhash_expect_key(us_proto, ui_src, ui_dest, us_dport, arr_hash_key, sizeof(arr_hash_key));
    if(i_hash_key_len <=0)
        return FR_FAIL;

    pthread_spin_lock(&g_spin_exp_lock);
    p_st_node = frhash2_getStr(&st_hash_expect_ct, arr_hash_key, i_hash_key_len);
    p_st_exp = (Frexp *)FRLIST_GET_NODE_DATA(p_st_node);
    if(p_st_exp == NULL)
    {
        pthread_spin_unlock(&g_spin_exp_lock);
        return FR_FAIL;
    }

    //减少关联连接的引用次数
    __sync_fetch_and_sub(&p_st_exp->ui_exp_refcnt,1);
    //更改时钟,释放该节点
    if(p_st_exp->ui_exp_refcnt <= 0)
    {
        frtimer_del(p_st_exp->p_st_timer_entry->p_st_event);

        p_st_exp->p_st_timer_entry->st_delay.tv_sec = APM_TIMEOUT_DEL_SEC;
        p_st_exp->p_st_timer_entry->st_delay.tv_usec = 0;
        frtimer_add(p_st_exp->p_st_timer_entry->p_st_event, &p_st_exp->p_st_timer_entry->st_delay);
    }
    pthread_spin_unlock(&g_spin_exp_lock);

    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  删除节点,并释放节点的资源
*  ARGS     :  p_st_node    (Frlist_node *)
*  RTN      :  FR_SUCC:成功, FR_FAIL:失败
*  NOTE     :
*******************************************************************************/
//int frct_expect_del(Frlist_node *p_st_node)
//{
//    int i_ret = FR_FAIL;
//
//    if(p_st_node == NULL)
//        return i_ret;
//
//    i_ret = frhash_del(&st_hash_expect_ct, p_st_node, __list_node_free);
//    return i_ret;
//}

/*******************************************************************************
*  FUNC     :  关联连接内容出力
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
void frct_expect_output(char **pp_result)
{
    char *p_outbuff=NULL;
    int i_outbuff_size =1024000;

    p_outbuff = frmalloc(i_outbuff_size);
    if(p_outbuff==NULL)
    {
        FRLOG_ERROR_DETAIL("frmalloc err");
        return;
    }

    *p_outbuff = '\0';

    pthread_spin_lock(&g_spin_exp_lock);
    frhash2_show(&st_hash_expect_ct, p_outbuff, i_outbuff_size, __list_node_show);
    pthread_spin_unlock(&g_spin_exp_lock);
    if(pp_result == NULL)
    {
        //FRLOG_INFO("==========================");
        //FRLOG_INFO("output ct expert data start\n%s", p_outbuff);
        //FRLOG_INFO("==========================");
        frdbg("==========================\n");
        frdbg("output ct expert data start\n%s\n", p_outbuff);
        frdbg("==========================\n");
        frfree(p_outbuff);
    }
    else
        *pp_result = p_outbuff;
    return;
}

/*******************************************************************************
*  FUNC     :  关联连接资源释放
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
void frct_expect_destory(void)
{
    frhash2_free(&st_hash_expect_ct, __list_node_free);
    pthread_spin_destroy(&g_spin_exp_lock);
    return;
}
